package top.tenebrous.termbor.rpc.registry.nacos;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.google.gson.Gson;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.x.discovery.ServiceInstance;
import top.tenebrous.termbor.rpc.common.helper.RpcServiceHelper;
import top.tenebrous.termbor.rpc.common.utils.SnowFlakeUtil;
import top.tenebrous.termbor.rpc.loadbalance.ServiceLoadBalance;
import top.tenebrous.termbor.rpc.loadbalance.random.RandomServiceLoadBalance;
import top.tenebrous.termbor.rpc.protocol.meta.ServiceMeta;
import top.tenebrous.termbor.rpc.registry.RegistryService;
import top.tenebrous.termbor.rpc.registry.config.RegistryConfig;
import top.tenebrous.termbor.rpc.spi.SPIClass;
import top.tenebrous.termbor.rpc.spi.loader.ExtensionLoader;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: termbor-rpc
 * @description:
 * @author: yazhang6
 * @create: 2024-05-23 16:41
 **/
@SPIClass
public class NacosRegistryService implements RegistryService {

    private NamingService namingService;

    private ServiceLoadBalance<ServiceMeta> serviceLoadBalance;

    @Override
    public void init(RegistryConfig registryConfig) throws Exception {
        try {
            this.namingService = NamingFactory.createNamingService(registryConfig.getRegistryAddr());
            this.serviceLoadBalance = ExtensionLoader.getExtension(ServiceLoadBalance.class, registryConfig.getRegistryLoadBalanceType());
        } catch (NacosException e) {
            throw new RuntimeException("连接到Nacos时发生错误");
        }
    }

    @Override
    public void register(ServiceMeta serviceMeta) throws Exception {

        Instance instance = new Instance();
        instance.setInstanceId(String.valueOf(SnowFlakeUtil.getSnowFlakeId()));
        instance.setIp(serviceMeta.getServiceAddr());
        instance.setPort(serviceMeta.getServicePort());
        instance.setHealthy(true);
        instance.setServiceName(RpcServiceHelper.buildServiceKey(serviceMeta.getServiceName(), serviceMeta.getServiceVersion(), serviceMeta.getServiceGroup()));
        Map<String, String> meta = new HashMap<String, String>() {{
            put(instance.getServiceName(), new Gson().toJson(serviceMeta));
        }};
        instance.setMetadata(meta);
        instance.setEnabled(true);
        namingService.registerInstance(
            RpcServiceHelper.buildServiceKey(serviceMeta.getServiceName(), serviceMeta.getServiceVersion(), serviceMeta.getServiceGroup()),
            instance);
    }

    @Override
    public void unRegister(ServiceMeta serviceMeta) throws Exception {
        try {
            namingService.deregisterInstance(RpcServiceHelper.buildServiceKey(serviceMeta.getServiceName(), serviceMeta.getServiceVersion(), serviceMeta.getServiceGroup()),
                serviceMeta.getServiceGroup(),
                serviceMeta.getServiceAddr(),
                serviceMeta.getServicePort());
        } catch (NacosException e) {
            throw new RuntimeException("注销服务失败");
        }
    }

    @Override
    public ServiceMeta discovery(String serviceName, int invokerHashCode) throws Exception {
        List<Instance> instances = namingService.getAllInstances(serviceName);

        if (CollUtil.isNotEmpty(instances)) {

            List<ServiceMeta> serviceInstances = instances.stream().map(item -> {
                Map<String, String> metadata = item.getMetadata();
                String serviceMetaJson = metadata.get(serviceName);
                System.out.println("注册中心的服务数据: " + serviceMetaJson);
                if (StringUtils.isNotBlank(serviceMetaJson)) {
                    return new Gson().fromJson(serviceMetaJson, ServiceMeta.class);
                }
                return null;
            }).collect(Collectors.toList());

            if (CollUtil.isNotEmpty(serviceInstances)) {
                return
                    serviceLoadBalance.select(serviceInstances, invokerHashCode);
            }
        }
        return null;
    }

    @Override
    public void destroy() throws IOException {

    }
}
